package com.runlooker.runer.service;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.filetransfer.FileTransfer.Status;
import org.jivesoftware.smackx.ping.PingFailedListener;
import org.jivesoftware.smackx.ping.PingManager;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;

import com.runlooker.runer.application.MyApplication;
import com.runlooker.runer.asmack.AsmackImpl;
import com.runlooker.runer.broadcast.SysBroadcastReceiver;
import com.runlooker.runer.dao.ChatMsgDao;
import com.runlooker.runer.dao.PersonInfoDao;
import com.runlooker.runer.dao.PresenceDao;
import com.runlooker.runer.dao.RosterEntryDao;
import com.runlooker.runer.db.RunerDBOpenHelper;
import com.runlooker.runer.entity.ChatMsgEntity;
import com.runlooker.runer.entity.PersonInfoEntity;
import com.runlooker.runer.entity.PresenceEntity;
import com.runlooker.runer.interfaces.FileReciveStatusListener;
import com.runlooker.runer.interfaces.FileSendStantusListener;
import com.runlooker.runer.interfaces.IConnectionStatusListener;
import com.runlooker.runer.interfaces.MessageListener;
import com.runlooker.runer.interfaces.NetEventHandlerListener;
import com.runlooker.runer.interfaces.PresenceChangeListener;
import com.runlooker.runer.interfaces.RosterChangeListener;
import com.runlooker.runer.util.L;
import com.runlooker.runer.util.NetworkUtil;
import com.runlooker.runer.util.PreferenceUtils;

public class XmppService extends Service implements NetEventHandlerListener {
	private static final String tag = XmppService.class.getSimpleName();
	public static final int CONNECTED = 0;// 连接成功
	public static final int DISCONNECTED = -1;// 连接失败
	public static final int CONNECTING = 1;// 连接中

	public static final int PING_TIMEOUT = 30 * 1000;// ping包的超时间隔
	public static final int PACKET_TIMEOUT = 180 * 1000;// 与服务器联系的最长时间间隔

	private Thread mLoginThread;

	public static final String NETWORK_ERROR = "NETWORK_ERROR";// 网络错误
	public static final String XMPPEXCEPTION = "XMPPException";// 异常
	public static final String LOGOUT = "LOGOUT";// 手动退出
	public static final String LOGIN_FAILED = "LOGIN_FAILED";// 登录失败
	public static final String PING_ALARM = "COM.RUNLOOKER.RUNER.PING_ALARM";
	// 发送文件
	public static String FILE_ROOT_PATH = Environment
			.getExternalStorageDirectory().getPath() + "/chat/file";
	public static String RECORD_ROOT_PATH = Environment
			.getExternalStorageDirectory().getPath() + "/chat/record";

	static {
		File root = new File(FILE_ROOT_PATH);
		root.mkdirs();// 没有根目录创建根目录
		root = new File(RECORD_ROOT_PATH);
		root.mkdirs();
	}
	private IBinder mBinder = new XMPPBinder();

	private PersonInfoDao mPersonInfoDao;
	private ChatMsgDao mChatMsgDao;
	private PresenceDao mPresenceDao;
	private RosterEntryDao mRosterEntryDao;

	private Handler mMainHandler = new Handler();
	private static int mConnectedState = DISCONNECTED; // 是否已经连接
	private AsmackImpl mAsmackImpl;
	private PingManager mPingManager;
	private PingFailedListener mPingFailedListener;

	private AlarmManager mAlarmManager;
	private PendingIntent mPingPendingIntent;
	private PingAlarmReceiver mPingAlarmReceiver;

	private Intent mPingIntent = new Intent(PING_ALARM);
	private IntentFilter mPingIntentFilter = new IntentFilter(PING_ALARM);

	// 各种回调接口
	private static List<FileSendStantusListener> mFileSendStantusCallBackList = new ArrayList<FileSendStantusListener>();
	private static List<FileReciveStatusListener> mFileReciveStantusCallbackList = new ArrayList<FileReciveStatusListener>();
	private static List<RosterChangeListener> mRosterChangeCallbackList = new ArrayList<RosterChangeListener>();
	private static List<PresenceChangeListener> mPresenceChangeCallbackList = new ArrayList<PresenceChangeListener>();
	private static List<MessageListener> mMessageCallbackList = new ArrayList<MessageListener>();
	private static List<IConnectionStatusListener> mConnectionStatusCallbackList = new ArrayList<IConnectionStatusListener>();

	public static void addFileSendStatusListener(FileSendStantusListener fc) {
		if (mFileSendStantusCallBackList.contains(fc)) {
			mFileSendStantusCallBackList.remove(fc);
		}
		mFileSendStantusCallBackList.add(fc);
	}

	public static void removeFileSendStatusListener(FileSendStantusListener fc) {
		mFileSendStantusCallBackList.remove(fc);
	}

	public static void removeAllFileSendStatusListener() {
		mFileSendStantusCallBackList.clear();
	}

	public static void addFileReciveStatusListener(FileReciveStatusListener fc) {
		if (mFileReciveStantusCallbackList.contains(fc)) {
			mFileReciveStantusCallbackList.remove(fc);
		}
		mFileReciveStantusCallbackList.add(fc);
	}

	public static void removeFileReciveStatusListener(
			FileReciveStatusListener fc) {
		mFileReciveStantusCallbackList.remove(fc);
	}

	public static void removeAllFileReciveStatusListener() {
		mFileReciveStantusCallbackList.clear();
	}

	public static void addMessageListener(MessageListener mc) {
		if (mMessageCallbackList.contains(mc)) {
			mMessageCallbackList.remove(mc);
		}
		mMessageCallbackList.add(mc);
	}

	public static void removeMessagelistener(MessageListener mc) {
		mMessageCallbackList.remove(mc);
	}

	public static void removeAllMessagelistener() {
		mMessageCallbackList.clear();
	}

	public static void addPresenceChangelistener(PresenceChangeListener rc) {
		if (mPresenceChangeCallbackList.contains(rc)) {
			mPresenceChangeCallbackList.remove(rc);
		}
		mPresenceChangeCallbackList.add(rc);
	}

	public static void removePresenceChangelistener(PresenceChangeListener rc) {
		mPresenceChangeCallbackList.remove(rc);
	}

	public static void removeAllPresenceChangelistener() {
		mPresenceChangeCallbackList.clear();
	}

	public static void addRosterChangelistener(RosterChangeListener rc) {
		if (mRosterChangeCallbackList.contains(rc)) {
			mRosterChangeCallbackList.remove(rc);
		}
		mRosterChangeCallbackList.add(rc);
	}

	public static void removeRosterChangelistener(RosterChangeListener rc) {
		mRosterChangeCallbackList.remove(rc);
	}

	public static void removeAllRosterChangelistener() {
		mRosterChangeCallbackList.clear();
	}

	/**
	 *
	 * AsmackImpl接收文件监听接收到文件后，反向调用该方法
	 *
	 * @param entity
	 */
	public void reciveFileStatus(ChatMsgEntity entity, Status status) {
		if (mFileReciveStantusCallbackList != null
				&& !mFileReciveStantusCallbackList.isEmpty()) {
			L.i(tag, "接收文件回调接口");
			for (FileReciveStatusListener recive : mFileReciveStantusCallbackList) {
				recive.fileReciveStatus(entity, status);
			}
		}
	}

	/**
	 * 
	 * 获得发送文件的状态
	 * 
	 * @param entity
	 */
	public void sendFileStatus(ChatMsgEntity msg, Status mStatus) {
		if (mFileSendStantusCallBackList != null
				&& !mFileSendStantusCallBackList.isEmpty()) {
			for (FileSendStantusListener status : mFileSendStantusCallBackList) {
				status.fileSendStatus(msg, mStatus);
			}
		}
	}

	public void recivedFileStatus(ChatMsgEntity msg, Status mStatus,
			double progress) {
	}

	/**
	 * 发送文件
	 * 
	 * 
	 * @param entity
	 * @throws XMPPException
	 */
	public void sendFile(ChatMsgEntity entity) throws XMPPException {
		mAsmackImpl.sendFile(entity);
	}

	public static int getConnectedStatus() {
		return mConnectedState;
	}

	/**
	 * 界面连接状态回调
	 * 
	 * @param cb
	 */
	public static void registerConnectionStatusCallback(
			IConnectionStatusListener cb) {
		if (mConnectionStatusCallbackList.contains(cb)) {
			mConnectionStatusCallbackList.remove(cb);
		}
		mConnectionStatusCallbackList.add(cb);
	}

	public static void unRegisterConnectionStatusCallback(
			IConnectionStatusListener cb) {
		mConnectionStatusCallbackList.remove(cb);
	}

	public static void removeAllConnectionStatusCallback() {
		mConnectionStatusCallbackList.clear();
	}

	public class XMPPBinder extends Binder {
		public XmppService getService() {
			return XmppService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		if (mConnectedState == DISCONNECTED
				&& MyApplication.isAccountConfigured(this)) {
			loginThread();
		}
		return mBinder;
	}

	@Override
	public void onRebind(Intent intent) {
		if (mConnectedState == DISCONNECTED
				&& MyApplication.isAccountConfigured(this)) {
			loginThread();
		}
		super.onRebind(intent);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		return true;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		mAsmackImpl = AsmackImpl.getInstance(this);
		mPingManager = PingManager.getInstanceFor(mAsmackImpl.getConnect());
		mPersonInfoDao = new PersonInfoDao(this);
		mChatMsgDao = new ChatMsgDao(this);
		mPresenceDao = new PresenceDao(this);
		mRosterEntryDao = new RosterEntryDao(this);

		SysBroadcastReceiver.addNetworkListener(this);
		mPingPendingIntent = PendingIntent
				.getBroadcast(this, 0, mPingIntent, 0);
		mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); // registerReceiver(mReloginAlarmReceiver,

		mPingAlarmReceiver = new PingAlarmReceiver();
		mPingManager.setPingIntervall(PING_TIMEOUT);
		registerReceiver(mPingAlarmReceiver, mPingIntentFilter);
		mPingFailedListener = new PingFailedListener() {
			@Override
			public void pingFailed() {
				mAlarmManager.cancel(mPingPendingIntent);
				try {
					if (mConnectedState == DISCONNECTED
							&& MyApplication
									.isAccountConfigured(XmppService.this)) {
						loginThread();
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		mPingManager.registerPingFailedListener(mPingFailedListener);

	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		if (MyApplication.isAccountConfigured(this)
				&& mConnectedState == DISCONNECTED) {
			loginThread();
		}
		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		try {
			logout();
		} catch (XMPPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		removeAllFileSendStatusListener();
		removeAllFileReciveStatusListener();
		removeAllMessagelistener();
		removeAllPresenceChangelistener();
		removeAllRosterChangelistener();
		removeAllConnectionStatusCallback();
		unregisterReceiver(mPingAlarmReceiver);
		mPingManager.unregisterPingFailedListener(mPingFailedListener);
		SysBroadcastReceiver.removeAllNetworkListener();
		SQLiteDatabase db = RunerDBOpenHelper.getInstance(this)
				.getReadableDatabase();
		db.close();
		super.onDestroy();

	}

	public void clearDataBase() {
		mPersonInfoDao.clearTable();
		mChatMsgDao.clearTable();
		mPresenceDao.clearTable();
		mRosterEntryDao.clearTable();
	}

	public void deleteAllInfoByAccount(String account) {
		mPersonInfoDao.deleInfoByAccount(account);
		mPresenceDao.deleInfoByAccount(account);
		mRosterEntryDao.deleInfoByAccount(account);
		mChatMsgDao.deleInfoByAccount(account);
	}

	/**
	 * 
	 * UPDATE_ROSTER = "update_roster"： 表示更新联系人。 DELETE_ROSTER
	 * ="delete_roster"：表示删除联系人。 ADD_ROSTER = "add_roster"：表示添加联系人
	 * 
	 * @param action
	 * @param arg0
	 */
	public void rosterChange() {
		if (mRosterChangeCallbackList != null
				&& !mRosterChangeCallbackList.isEmpty()) {
			for (int i = 0; i < mRosterChangeCallbackList.size(); i++) {
				mRosterChangeCallbackList.get(i).rosterChange();
			}
		}
	}

	public void precenceChange(PresenceEntity entity) {
		if (mPresenceChangeCallbackList != null
				&& !mPresenceChangeCallbackList.isEmpty()) {
			for (int i = 0; i < mPresenceChangeCallbackList.size(); i++) {
				mPresenceChangeCallbackList.get(i).PresenceChange(entity);
			}
		}
	}

	/**
	 * 非UI线程连接失败反馈
	 * 
	 * @param reason
	 */
	public void postConnectionFailed(final String reason) {
		mMainHandler.post(new Runnable() {
			public void run() {
				L.i(tag, "登录失败：" + reason);
				mConnectedState = DISCONNECTED;// 更新当前连接状态
				mAlarmManager.cancel(mPingPendingIntent);
				// 回调
				if (mConnectionStatusCallbackList != null
						&& !mConnectionStatusCallbackList.isEmpty()) {
					for (IConnectionStatusListener callback : mConnectionStatusCallbackList) {
						callback.connectionStatusChanged(mConnectedState,
								reason);
					}
				}
			}
		});
	}

	public void postConnectionScuessed() {
		mMainHandler.post(new Runnable() {
			public void run() {
				L.i(tag, "登录成功");
				mConnectedState = CONNECTED;// 已经连接上
				mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
						System.currentTimeMillis() + 3000, PACKET_TIMEOUT,
						mPingPendingIntent);
				if (mConnectionStatusCallbackList != null
						&& !mConnectionStatusCallbackList.isEmpty()) {
					for (IConnectionStatusListener callback : mConnectionStatusCallbackList) {
						callback.connectionStatusChanged(mConnectedState, "");
					}
				}
			}

		});
	}

	// 连接中，通知界面线程做一些处理
	public void postConnecting() {
		L.i(tag, "登录中");
		mMainHandler.post(new Runnable() {
			public void run() {
				mConnectedState = CONNECTING;// 连接中
				if (mConnectionStatusCallbackList != null
						&& !mConnectionStatusCallbackList.isEmpty()) {
					for (IConnectionStatusListener callback : mConnectionStatusCallbackList) {
						callback.connectionStatusChanged(mConnectedState, "");
					}
				}
			}
		});
	}

	/**
	 * 登录线程
	 */
	private void loginThread() {
		if (mLoginThread == null) {
			mLoginThread = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						login();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					if (mLoginThread != null) {
						synchronized (mLoginThread) {
							mLoginThread = null;
						}
					}
				}
			});
			mLoginThread.start();
		}
	}

	@Override
	public void onNetChange() {
		if (!(NetworkUtil.getNetworkState(this) == NetworkUtil.NETWORN_NONE)) {
			if (mConnectedState == DISCONNECTED
					&& MyApplication.isAccountConfigured(this)) {
				loginThread();
			}
		}
	}

	/**
	 * 本机帐号密码，登录操作
	 * 
	 * @throws Exception
	 */
	public synchronized void login() throws Exception {
		if (mConnectedState == DISCONNECTED) {
			String account = PreferenceUtils.getPrefString(this,
					MyApplication.ACCOUNT_USERNAME_KEY, "");
			String password = PreferenceUtils.getPrefString(this,
					MyApplication.ACCOUNT_PASSWORD_KEY, "");
			loginService(account, password);
		}
	}

	/**
	 * 
	 * 登录服务器，并更新服务器的一些数据（当前手机的信息，个人信息等）
	 * 
	 * @param account
	 * @param password
	 * @throws Exception
	 * @throws XMPPException
	 */
	public void loginService(final String account, final String password)
			throws Exception {
		if (mConnectedState == DISCONNECTED) {
			if (NetworkUtil.getNetworkState(this) == NetworkUtil.NETWORN_NONE) {
				postConnectionFailed(NETWORK_ERROR);
				throw new Exception("没有网络");
			}
			postConnecting();
			try {
				mAsmackImpl.login(account, password);
				postConnectionScuessed();
			} catch (Exception e) {
				e.printStackTrace();
				postConnectionFailed(XMPPEXCEPTION);
				throw new Exception("登录出错");
			}

		}
	}

	public void register(String account, String password,
			Map<String, String> attributes) throws Exception {
		mAsmackImpl.register(account, password, attributes);
	}

	public void logout() throws XMPPException {
		if (mConnectedState != CONNECTING) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						mAsmackImpl.logout();
						postConnectionFailed(LOGOUT);// 手动退出
					} catch (XMPPException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
	}

	public boolean isLogin() {
		return mConnectedState == CONNECTED ? true : false;
	}

	/**
	 * 
	 * 搜索帐号,返回搜索的帐号，则标识服务器有该帐号，返回null则表示没有该帐号。抛出异常标识搜索失败
	 * 
	 * @param account
	 * @return
	 * @throws XMPPException
	 */
	public String searchAccount(String account) throws Exception {
		return mAsmackImpl.searchAccount(account);
	}

	public void addRosterItem(String account, String alias)
			throws XMPPException {
		mAsmackImpl.addRosterItem(account, alias);
	}

	public void removeRosterItem(String account) throws XMPPException {
		mAsmackImpl.removeRosterItem(account);
	}

	public void renameRosterItem(String oldName, String newName)
			throws XMPPException {
		mAsmackImpl.renameRosterItem(oldName, newName);
	}

	public void moveRosterItemToGroup(String user, String group)
			throws XMPPException {
		mAsmackImpl.moveRosterItemToGroup(user, group);
	}

	public void sendOfflineMessages() {

	}

	public Collection<RosterEntry> getEntities() {
		return mAsmackImpl.getEntities();
	}

	public void renameRosterGroup(String group, String newGroup) {
		mAsmackImpl.renameRosterGroup(group, newGroup);
	}

	/**
	 * 
	 * 请求添加好友
	 * 
	 * @param user
	 */
	public void requestAuthorizationForRosterItem(String user) {
		mAsmackImpl.requestAuthorizationForRosterItem(user);
	}

	public void addRosterGroup(String group) {
		mAsmackImpl.addRosterGroup(group);
	}

	public void removeRosterGroup(String group) {

	}

	public Message sendChatMessage(ChatMsgEntity entity) throws XMPPException {
		return mAsmackImpl.sendChatMessage(entity);
	}

	public void processMessage(ChatMsgEntity ChatMsgEntity) {
		if (mMessageCallbackList != null && !mMessageCallbackList.isEmpty()) {
			for (MessageListener callback : mMessageCallbackList) {
				callback.precessMesaage(ChatMsgEntity);
			}
		}
	}

	public void savePersonInfo(PersonInfoEntity info) throws XMPPException {
		mAsmackImpl.savePersonInfo(info);
	}

	public PersonInfoEntity getOurPersonInfo() throws XMPPException {
		return mAsmackImpl.getOurPersonInfo();
	}

	public PersonInfoEntity getPersonInfoByAccount(String account)
			throws XMPPException {
		return mAsmackImpl.getPersonInfoByAccount(account);
	}

	public void sendPacket(Packet packet) {
		mAsmackImpl.sendPacket(packet);
	}

	/**
	 * 
	 * 返回null或者XMPPConnection实例
	 * 
	 * @return
	 */
	public XMPPConnection getConnect() {
		return mAsmackImpl.getConnect();
	}

	public Presence getPresenceOfAccount(String Account) {
		return mAsmackImpl.getPresenceOfAccount(Account);
	}

	private class PingAlarmReceiver extends BroadcastReceiver {
		public void onReceive(Context ctx, Intent i) {
			mAsmackImpl = AsmackImpl.getInstance(XmppService.this);
			// 当超过PACKET_TIMEOUT时间未与服务器通讯，才向服务器发送ping包
			if ((System.currentTimeMillis() - mAsmackImpl
					.getLastReceivePacketTime()) > PACKET_TIMEOUT) {
				mPingManager.pingMyServer();
			}
		}
	}

}
